//!!
try {
  
function is_array (a) { return (typeof a == "object") && (a instanceof Array); }

function createXMLHttp()
{
  var xmlHttp = false;
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
     try
     {
       xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); 
     }
     catch (e)
     {
       try
       {
         xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
       }
       catch (e2)
       {
         xmlHttp = false;
       }
     }
  @end @*/

  if (!xmlHttp && typeof XMLHttpRequest != 'undefined') 
  {
    xmlHttp = new XMLHttpRequest();
  }
  return xmlHttp;
}

function XmlHttpQueue_onReadyStateChange()
{
  
  if ( this.readyState == 4)
  {
    if ( this.status==0 || this.status==200 )
    {
      this.owner._ready();
    }
    else
    {
      alert("HTTP error: " + this.statusText);
    }
  }
}

function XmlHttpQueue(userName, password)
{
  this.userName = userName;
  this.password = password;
  this.xmlHttp = createXMLHttp();
  this.xmlHttp.onreadystatechange = XmlHttpQueue_onReadyStateChange;
  this.xmlHttp.owner = this;
  this.currentCallback = null;

  this.abort = function()
  {
    this.xmlHttp.abort();
  }

  this._ready = function()
  {
    /*if ( this.xmlHttp.responseText.search("google") != -1 )
      alert( "XMLHTTP: " + this.xmlHttp.responseText );
      */
    
    if ( this.currentCallback )
      this.currentCallback( this.xmlHttp );
  }

  this.get = function (url, callback)
  {
    this.currentCallback = callback;
    this.xmlHttp.open('GET', url, true);
    this.xmlHttp.send(null);
  }

  this.post = function (url, postData, callback)
  {
    this.currentCallback = callback;
    this.xmlHttp.open('POST', url, true);
    this.xmlHttp.send(postData);
  }
}

/////// /////////////////////////
/////// /////////////////////////

function JSONMethod(method_name, param_data)
{
  return JSON.stringify(
      {
        jsonrpc:"2.0",
        method: method_name,
        params: param_data,
        id:1
      }
    );
}

function JSONNotify(method_name, param_data)
{
  return JSON.stringify(
    {
      jsonrpc:"2.0",
      method: method_name,
      params: param_data
    }
  );
}

function JSONEval( json )
{
  return eval("("+json+")");
}

/// //////////////////////////////
/// //////////////////////////////
/// //////////////////////////////
function ChannelManager()
{
  this.subscribers = {};
  this.openWait = [];
  this.closeWait = [];

  this.open = function( channel )
  {
    this.openWait = this.openWait.concat( channel );
    /*if ( !is_array(channel) )
      this.openWait.push(channel);
      */
  }

  this.close = function( channelName )
  {
    this.closeWait.push(channelName);
  }

  this.openWaitCount = function()
  {
    return this.openWait.length;
  }

  this.closeWaitCount = function()
  {
    return this.closeWait.length;
  }

  this.subscribe = function( channelName, callback )
  {
    if ( !this.subscribers[channelName] )
      this.subscribers[channelName]=[];
    this.subscribers[channelName].push(callback);
  }

  this.describe = function( channelName, callback )
  {
    var subs = this.subscribers[channelName];
    if (!subs) return;
    
    for (var i=0; i < subs.length; ++i )
    {
      if ( subs[i] == callback )
      {
        //delete subs[i];
        // subs[i] = 0;
        subs.splice(i,1)
        break;
      }
    }
  }

  this.fire = function( result )
  {
    
    for (var i in result)
    {
      var channelName = result[i].channel;
      
      if ( channelName in this.subscribers )
      {
        var subs = this.subscribers[ channelName ];
        for ( var j = 0 ; j < subs.length; j++)
	{
	  
          try { subs[j]( result[i] ); }catch(e) {alert(e.message)}
	}
      }
    }
  }
}

/// //////////////////////////////
/// //////////////////////////////

function PublishManager()
{
  this.publishWait = [];
  
  this.publish = function(messages)
  {
    for (var i = 0 ; i < messages.length; ++i)
    {
      var ready = false;
      for (var j = 0 ; j < this.publishWait.length; ++j)
      {
        if ( this.publishWait[j].channel == messages[i].channel)
        {
          ready = true;
          this.publishWait[j] = messages[i];
        }
      }
      if (!ready)
        this.publishWait.push(messages[i]);
    }
    // this.publishWait = this.publishWait.concat(messages);
  }

  this.publishWaitCount = function() { return this.publishWait.length;}
  
  this.detachMessageList = function()
  {
    var messageList = this.publishWait;
    this.publishWait = [];
    return messageList;
  }
  
  this.simplePublish = function(channelName, contentData)
  {
    this.publish( {channel: channelName, content: contentData, limit:10, lifetime:60 } );
    /*this.publishWait[channelName] =
        [
          {
            channel: channelName,
            content: contentData,
            limit:10,
            lifetime:60
          }
        ];
	*/
    
  }

   
    /*
  this.publishWait = {};

  this.publishWaitCount = function()
  {
    var count = 0;
    for (k in this.publishWait) if (this.publishWait.hasOwnProperty(k)) count++;
    return count;
  }

  this.detachMessageList = function()
  {
    var messageList = [];
    for ( var i in  this.publishWait)
    {
      for (var j in this.publishWait[i])
        messageList.push(this.publishWait[i][j]);
    }
    this.publishWait = {};
    return messageList;
  }

  this.simplePublish = function(channelName, contentData)
  {
    this.publishWait[channelName] =
        [
          {
            channel: channelName,
            content: contentData,
            limit:10,
            lifetime:60
          }
        ];
    
  }
  */
  
}


/// //////////////////////////////
/// //////////////////////////////

function Comet_onReadyStateChange(xmlHttp)
{
  this.owner._callback(xmlHttp);
}

function Comet_onCreate(xmlHttp)
{
  this.owner.bussy = false;
  this.owner.confirm = false;
  var response = JSONEval(xmlHttp.responseText)
  this.owner.uuid = response.result;
  this.owner.process();
}

function Comet_onOpen(xmlHttp)
{
  try{
    
    if ( !xmlHttp.responseText )
      return;
  if (!this.owner.bussy)
    alert("fuck " + xmlHttp.responseText)
  var response = JSONEval(xmlHttp.responseText)
  this.owner.bussy = false;
  this.owner.confirm = false;
  this.owner.process();
  }
  catch(e)
  {
    
   /* alert("БЛЯ: " + e.message);
    alert("БЛЯ: " + xmlHttp.responseText);*/
    
  }
  // Ахтуенг - рекурсия, убрать
  // this.owner.process();
}

function Comet_onPublished(xmlHttp)
{
  if ( !xmlHttp.responseText )
    return;
  var response = JSONEval(xmlHttp.responseText);
  this.owner.bussy = false;
  this.owner.confirm = false;
  this.owner.process();
}

function Comet_onSubscribe(xmlHttp)
{
  if ( !xmlHttp.responseText )
    return;
  var response = JSONEval(xmlHttp.responseText)
  this.owner.bussy = false;
  this.owner.confirm = true;
  this.owner.channelManager.fire(response.result);
  this.owner.process();
  // Ахтуенг - рекурсия, убрать
  // this.owner.process(response.result);
}
    

/**
  get()
  post()
  create()
  open()
  close()
  subscribe()
  describe()
  publish()
*/
function Comet( userName, password )
{
  this.xmlHttpQueue = new XmlHttpQueue( userName, password );
  this.xmlHttpQueue.owner = this;
  this.currentCallback = null;
  this.cometUrl = "/json_rpc?oid=-1&sid=test";
  this.channelManager = new ChannelManager();
  this.publishManager = new PublishManager();
  this.bussy = true;
  this.confirm = true;

  this.process = function()
  {
    try
    {
      if ( this.bussy )
        return;

      if ( this.channelManager.openWaitCount() > 0 )
      {
        var channelList = this.channelManager.openWait;
        this.channelManager.openWait = [];
        this.bussy = true;
        // this.xmlHttpQueue.abort();
        // есть каналы на открытие
        this.xmlHttpQueue.post(
          this.cometUrl,
          JSONMethod(
            "open",
            {
              key : "",
              uuid: this.uuid,
              channels: channelList
            }
          ),
          Comet_onOpen
        );
        
        return;
      }

      if ( this.channelManager.closeWaitCount() > 0 )
      {
        this.xmlHttpQueue.abort();
        this.channelManager.closeWait = [];
        //alert("comet.close not impl");
        return;
      }

      // проверим publish
      if ( this.publishManager.publishWaitCount() > 0 )
      {
        this.bussy = true;
        //this.xmlHttpQueue.abort();
        var messageList = this.publishManager.detachMessageList();

        // alert( JSON.stringify( messageList ) );
        this.xmlHttpQueue.post(
          this.cometUrl,
          JSONMethod(
            "publish",
            {
              key : "",
              uuid: this.uuid,
              messages: messageList
            }
          ),
          Comet_onPublished
        );
        return;
      }

      // ну теперь можно и subscribe
      // subscribe можно прерывать в любой момент
      this.bussy = false;
      //this.xmlHttpQueue.abort();
      this.xmlHttpQueue.post(
        this.cometUrl,
        JSONMethod(
          "subscribe",
          {
            key : "",
            uuid: this.uuid,
            confirm: this.confirm
          }
        ),
        Comet_onSubscribe
      );
      this.confirm = false;
    }
    catch(e)
    {
      alert(e.message);
    }
    
  }

  this._callback = function( xmlHttp )
  {
    if ( this.currentCallback )
    {
      this.currentCallback( xmlHttp.responseText );
      this.currentCallback = null;
    }
  }

  this.get = function (url, callback)
  {
    this.currentCallback = callback;
    return this.xmlHttpQueue.get(url, Comet_onReadyStateChange);
  }

  this.create = function()
  {
    this.bussy = true;
    var create_params = 
    {
      key: "",
      lifetime: 60,
      timeout: 10,
      channels: [
        /*{
          channel:"test.channel1",
          min_size:1,
          max_size:10
        }*/
      ]
    }

    // this.createCallback = pcallback;
    //
  // {"jsonrpc":"2.0","method":"create","params":{"key":"","lifetime":60,"timeout":60,"channels":[]},"id":1}]
  // {"jsonrpc":"2.0","method":"create","params":{"key":"","lifetime":30,"timeout":10,"channels":[{"channel":"test.channel1","min_size":1,"max_size":10}]},"id":1}]


    this.xmlHttpQueue.post(
      this.cometUrl,
      JSONMethod("create", create_params),
      Comet_onCreate
    );
  }

  this.open = function( channel )
  {
    this.channelManager.open(channel);
    this.process();
  }

  this.close = function( channelName )
  {
    // alert("close: " + channelName);
    this.channelManager.close(channelName);
    this.process();
  }

  this.subscribe = function( channelName, callback )
  {
    this.channelManager.subscribe(channelName, callback);
    //this.process();
  }

  this.describe = function( channelName, callback )
  {
    this.channelManager.describe(channelName, callback);
    //this.process();
  }

  this.simplePublish = function(channelName, contentData)
  {
    this.publishManager.simplePublish(channelName, contentData);
    this.process();
  }

  this.publish = function(messages)
  {
    this.publishManager.publish(messages);
    this.process();
  }

}

}catch(e)
{
  alert(e.message);
}

